﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.IO;
using AForge.Imaging.Filters;

namespace QuickPhotoFilter
{
    enum Tool { Pen, Brush, Scale, Fill, Pipette, Eraser }
    // Fill - заливка

    class QPF
    {
        string sourceFileName = null;
        Byte[] pixelBuffer;//массив байтов, определяющих пиксели изображения
        WriteableBitmap writeableBitmap;//изображение
        Canvas canvas;
        Image mainImage = new Image();//отображает writeableBitmap
        Tool activeTool = Tool.Pen;
        double scale = 1;
        double zoomCoef = 2;
        int thickness = 0;//толщина (линий, точек и т.д.)
        Color currentColor1 = Colors.Black, currentColor2 = Colors.White;
        double initialScaleX, initialScaleY;//изначальное масштабирование изображения

        public QPF(Canvas workspace)
        {
            canvas = workspace;
            canvas.Width = 1024;
            canvas.Height = 768;
            canvas.Children.Add(mainImage);
            writeableBitmap = new WriteableBitmap((int)canvas.Width, (int)canvas.Height, 96, 96, PixelFormats.Bgra32, null);
            pixelBuffer = new Byte[(int)canvas.Width * (int)canvas.Height * 4];
            for (int i = 0; i < pixelBuffer.Length; i += 4)
                ColorPixel(pixelBuffer, i, Colors.White);
            writeableBitmap.WritePixels(new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight), pixelBuffer, writeableBitmap.PixelWidth * 4, 0);
            mainImage.Source = writeableBitmap;
        }

        public string FileName { get { return sourceFileName; } }

        public Tool ActiveTool
        {
            get { return activeTool; }
            set { activeTool = value; }
        }

        public double Scale
        {
            get { return scale; }
            private set
            {
                scale = value;
                canvas.LayoutTransform = new ScaleTransform(scale, scale);
            }
        }

        public int Thickness 
        {
            get { return thickness; }
            set { thickness = value; }
        }

        public Color CurrentColor1
        {
            get { return currentColor1; }
            set { currentColor1 = value; }
        }
        public Color CurrentColor2
        {
            get { return currentColor2; }
            set { currentColor2 = value; }
        }

        public double InitialScaleX { get { return initialScaleX; } }

        public double InitialScaleY { get { return initialScaleY; } }

        public Image MainImage { get { return mainImage; } }

        void ColorPixel(byte[] pixelArray, int index, Color color)
        {
            pixelArray[index] = color.B;
            pixelArray[index + 1] = color.G;
            pixelArray[index + 2] = color.R;
            pixelArray[index + 3] = color.A;
        }

        public bool LoadPictureFromFile(string fileName, out string result)
        {
            try
            {
                BitmapFrame bitmapFrame = BitmapFrame.Create(new MemoryStream(File.ReadAllBytes(fileName)));
                pixelBuffer = new Byte[bitmapFrame.PixelHeight * bitmapFrame.PixelWidth * 4];
                bitmapFrame.CopyPixels(pixelBuffer, bitmapFrame.PixelWidth * 4, 0);
                writeableBitmap = new WriteableBitmap(bitmapFrame);
                writeableBitmap.WritePixels(new Int32Rect(0, 0, bitmapFrame.PixelWidth, bitmapFrame.PixelHeight), pixelBuffer, bitmapFrame.PixelWidth * 4, 0);
            }
            catch (Exception exception)
            {
                result = exception.Message;
                return false;
            }
            canvas.Width = writeableBitmap.PixelWidth;
            canvas.Height = writeableBitmap.PixelHeight;
            mainImage.Source = writeableBitmap;
            initialScaleX = writeableBitmap.DpiX / 96;
            initialScaleY = writeableBitmap.DpiY / 96;
            mainImage.RenderTransform = new ScaleTransform(initialScaleX, initialScaleY);
            sourceFileName = fileName;
            Scale = 1;
            result = null;
            return true;
        }

        public bool SaveFileAs(string savedFileName, out string result)
        {
            try
            {
                string format = savedFileName.Substring(savedFileName.LastIndexOf('.') + 1);
                BitmapEncoder encoder;
                switch (format)
                {
                    case "bmp":
                        encoder = new BmpBitmapEncoder();
                        break;
                    case "jpeg":
                        encoder = new JpegBitmapEncoder();
                        break;
                    case "gif":
                        encoder = new GifBitmapEncoder();
                        break;
                    case "png":
                        encoder = new PngBitmapEncoder();
                        break;
                    case "tiff":
                        encoder = new TiffBitmapEncoder();
                        break;
                    default:
                        encoder = new JpegBitmapEncoder();
                        break;
                }
                encoder.Frames.Add(BitmapFrame.Create(writeableBitmap));
                using(FileStream fileStream = new FileStream(savedFileName, FileMode.Create))
                {
                    encoder.Save(fileStream);
                    fileStream.Close();
                }
            }
            catch (Exception exception)
            {
                result = exception.Message;
                return false;
            }
            result = null;
            sourceFileName = savedFileName;
            return true;
        }

        public void ZoomIn()
        {
            if (scale <= 8)
                Scale *= zoomCoef;
        }

        public void ZoomOut()
        {
            if (scale >= 0.125)
                Scale /= zoomCoef;
        }

        public Color DetermineColor(Point point) // определяет цвет пикселя основного изображения
        {
            int byteNumber = FirstByteNumber(PixelNumber(point, writeableBitmap.PixelWidth));
            return Color.FromArgb(pixelBuffer[byteNumber + 3], pixelBuffer[byteNumber + 2], pixelBuffer[byteNumber + 1], pixelBuffer[byteNumber]);
        }

        public static Color DetermineColor(Point point, byte[] picturePixelArray, int picturePixelWidth)
        {
            int byteNumber = PixelNumber(point, picturePixelWidth) * 4;
            return Color.FromArgb(picturePixelArray[byteNumber + 3], picturePixelArray[byteNumber + 2], picturePixelArray[byteNumber + 1], picturePixelArray[byteNumber]);
        }

        public int FirstByteNumber(int pixelNumber) { return pixelNumber * 4; }//определяет номер первого байта пикселя точки в одномерном массиве

        public static int PixelNumber(Point point, int picturePixelWidth) { return (int)point.Y * picturePixelWidth + (int)point.X; }//определяет номер пикселя точки в одномерном массиве для изображения шириной picturePixelWidth

        public Color InvertColor(Color color) { return Color.FromArgb(color.A, (byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B)); }

        public void DrawPoint(Point point, Color color)//рисует квадрат со сторонами, равными currentWidth * 2 + 1, с центром в point
        {
            int rectangleWidth = thickness * 2 + 1, rectangleHeight = thickness * 2 + 1;
            int x = (int)point.X - thickness, y = (int)point.Y - thickness;
            for (int i = (int)point.X - thickness, j = (int)point.Y - thickness; i <= (int)point.X + thickness; ++i, ++j)
            {
                if (i < 0)
                {
                    x++;
                    rectangleWidth--;
                }
                if (i >= writeableBitmap.PixelWidth)
                    rectangleWidth--;
                if (j < 0)
                {
                    y++;
                    rectangleHeight--;
                }
                if (j >= writeableBitmap.PixelHeight)
                    rectangleHeight--;
            }
            int offset = FirstByteNumber(PixelNumber(new Point(x, y), writeableBitmap.PixelWidth));
            for (int i = x; i < x + rectangleWidth; ++i)
                for (int j = y; j < y + rectangleHeight; ++j)
                {
                    int firstByteNumber = FirstByteNumber(PixelNumber(new Point(i, j), writeableBitmap.PixelWidth));
                    ColorPixel(pixelBuffer, firstByteNumber, color);
                }
            for (int i = 0; i < rectangleHeight; ++i)
                writeableBitmap.WritePixels(new Int32Rect(x, y + i, rectangleWidth, 1), pixelBuffer, rectangleWidth * 4, writeableBitmap.PixelWidth * 4 * i + offset);
        }

        public void DrawLine(Point currentPoint, Point previousPoint, Color color)
        {
            DrawPoint(currentPoint, color);
            if (currentPoint != previousPoint)
            {
                alglib.spline1d.spline1dinterpolant spline = new alglib.spline1d.spline1dinterpolant();
                double[] x = new double[] { previousPoint.X, currentPoint.X };
                double[] y = new double[] { previousPoint.Y, currentPoint.Y };
                double[] splineX;
                double[] splineY;
                if (Math.Abs(x[0] - x[1]) >= Math.Abs(y[0] - y[1]))
                {
                    splineX = x;
                    splineY = y;
                }
                else
                {
                    splineX = y;
                    splineY = x;
                }
                alglib.spline1d.spline1dbuildlinear(splineX, splineY, 2, spline);
                int i = (int)splineX[0];
                while (i != (int)splineX[1])
                {
                    Point point;
                    if (splineX == x)
                        point = new Point(i, alglib.spline1d.spline1dcalc(spline, i));
                    else
                        point = new Point(alglib.spline1d.spline1dcalc(spline, i), i);
                    DrawPoint(point, color);
                    if (splineX[1] > splineX[0])
                        ++i;
                    else
                        --i;
                }
            }
        }

        public bool Filter()
        {
            FilterWindow a = new FilterWindow();
            a.WriteableBitmap = writeableBitmap;
            if (a.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                for (int i = 0; i < a.InitialPicture.Width; ++i)
                    for (int j = 0; j < a.InitialPicture.Height; ++j)
                    {
                        System.Drawing.Color currentPixelColor = a.InitialPicture.GetPixel(i, j);
                        int firstByteNumber = FirstByteNumber(PixelNumber(new Point(i, j), writeableBitmap.PixelWidth));
                        pixelBuffer[firstByteNumber] = currentPixelColor.B;
                        pixelBuffer[firstByteNumber + 1] = currentPixelColor.G;
                        pixelBuffer[firstByteNumber + 2] = currentPixelColor.R;
                        pixelBuffer[firstByteNumber + 3] = currentPixelColor.A;
                    }
                writeableBitmap.WritePixels(new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight), pixelBuffer, writeableBitmap.PixelWidth * 4, 0);
            }
            //System.Drawing.Bitmap currentBitmap = new System.Drawing.Bitmap((int)canvas.Width, (int)canvas.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            //int width = currentBitmap.Width;
            //int height = currentBitmap.Height;
            //AForge.Imaging.Filters.StereoAnaglyph filter = new AForge.Imaging.Filters.StereoAnaglyph(StereoAnaglyph.Algorithm.ColorAnaglyph);
            //for (int i = 0; i < currentBitmap.Width; ++i)
            //    for (int j = 0; j < currentBitmap.Height; ++j)
            //    {
            //        Color currentPixelColor = DetermineColor(new Point(i, j));
            //        System.Drawing.Color color = System.Drawing.Color.FromArgb(currentPixelColor.A, currentPixelColor.R, currentPixelColor.G, currentPixelColor.B);
            //        currentBitmap.SetPixel(i, j, color);
            //    }
            //filter.ApplyInPlace(currentBitmap);
            //for (int i = 0; i < currentBitmap.Width; ++i)
            //    for (int j = 0; j < currentBitmap.Height; ++j)
            //    {
            //        System.Drawing.Color currentPixelColor = currentBitmap.GetPixel(i, j);
            //        int firstByteNumber = FirstByteNumber(PixelNumber(new Point(i, j), writeableBitmap.PixelWidth));
            //        pixelBuffer[firstByteNumber] = currentPixelColor.B;
            //        pixelBuffer[firstByteNumber + 1] = currentPixelColor.G;
            //        pixelBuffer[firstByteNumber + 2] = currentPixelColor.R;
            //        pixelBuffer[firstByteNumber + 3] = currentPixelColor.A;
            //    }
            //writeableBitmap.WritePixels(new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight), pixelBuffer, writeableBitmap.PixelWidth * 4, 0);
            return true;
        }

        public bool Insert(Shape shape)
        {
            Transform canvasLayoutTransform = canvas.LayoutTransform;
            canvas.LayoutTransform = null;
            Size canvasSize = new Size(writeableBitmap.PixelWidth, writeableBitmap.PixelHeight);
            canvas.Arrange(new Rect(canvasSize));
            canvas.Measure(canvasSize);
            shape.RenderTransform = mainImage.RenderTransform;
            Size size1 = new Size(shape.Width, shape.Height);
            shape.Arrange(new Rect(size1));
            shape.Measure(size1);
            mainImage.RenderTransform = null;
            Size size = new Size(writeableBitmap.PixelWidth, writeableBitmap.PixelHeight);
            mainImage.Arrange(new Rect(size));
            mainImage.Measure(size);
            RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(writeableBitmap.PixelWidth, writeableBitmap.PixelHeight, writeableBitmap.DpiX, writeableBitmap.DpiY, PixelFormats.Pbgra32);
            
            renderTargetBitmap.Render(shape);
            byte[] pixels = new Byte[writeableBitmap.PixelWidth * writeableBitmap.PixelHeight * 4];
            Color a = Colors.Black;
            renderTargetBitmap.CopyPixels(pixels, writeableBitmap.PixelWidth * 4, 0);



            //потом все убрать
            WriteableBitmap wb = new WriteableBitmap(writeableBitmap.PixelWidth, writeableBitmap.PixelHeight, writeableBitmap.DpiX, writeableBitmap.DpiY, PixelFormats.Pbgra32, null);
            wb.WritePixels(new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight), pixels, (int)canvas.Width * 4, 0);
            BitmapEncoder encoder = new JpegBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(wb));
            using (FileStream fileStream = new FileStream("C:\\Users\\Женя\\Desktop\\Picture.jpeg", FileMode.Create))
            {
                encoder.Save(fileStream);
                fileStream.Close();
            }


            int x = (int)Canvas.GetLeft(shape), y = (int)Canvas.GetTop(shape);
            for (int i = x; i < x + shape.Width; ++i)
                for (int j = y; j < y + shape.Height; ++j)
                {
                    int firstByteNumber = FirstByteNumber(PixelNumber(new Point(i, j), writeableBitmap.PixelWidth));
                    pixelBuffer[firstByteNumber] = pixels[firstByteNumber];
                    pixelBuffer[firstByteNumber + 1] = pixels[firstByteNumber + 1];
                    pixelBuffer[firstByteNumber + 2] = pixels[firstByteNumber + 2];
                    pixelBuffer[firstByteNumber + 3] = pixels[firstByteNumber + 3];
                    writeableBitmap.WritePixels(new Int32Rect(i, j, 1, 1), pixels, 4, firstByteNumber);
                }
            canvas.Children.Remove(shape);
            mainImage.RenderTransform = shape.RenderTransform;
            //canvas.LayoutTransform = canvasLayoutTransform;
            return true;



            //canvas.Children.Add(shape);
            //RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap((int)shape.Width, (int)shape.Height, writeableBitmap.DpiX, writeableBitmap.DpiY, PixelFormats.Pbgra32);
            //renderTargetBitmap.Render(shape);
            //byte[] pixels = new Byte[(int)shape.Width * (int)shape.Height * 4];
            //renderTargetBitmap.CopyPixels(pixels, (int)shape.Width * 4, 0);
            //int x = (int)Canvas.GetLeft(shape), y = (int)Canvas.GetTop(shape);
            //for (int i = x; i < x + shape.Width; ++i)
            //{
            //    int pbOffset = 4 * i * writeableBitmap.PixelWidth, pOffset = 4 * i * (int)shape.Width;
            //    for (int j = 0; j < renderTargetBitmap.PixelWidth * 4; j += 4)
            //    {
            //        int alpha = (int)pixels[j + pOffset + 3];
            //        pixelBuffer[j + pbOffset] = (byte)(pixels[j + pOffset] * alpha / 255);
            //        pixelBuffer[j + pbOffset + 1] = (byte)(pixels[j + pOffset + 1] * alpha / 255);
            //        pixelBuffer[j + pbOffset + 2] = (byte)(pixels[j + pOffset + 2] * alpha / 255);
            //        pixelBuffer[j + pbOffset + 3] = (byte)(alpha * alpha / 255);
            //    }
            //}
            //writeableBitmap.WritePixels(new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight), pixelBuffer, writeableBitmap.PixelWidth * 4, 0);
               
                
            //    //}
            ////writeableBitmap.WritePixels(new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight), pixelBuffer, writeableBitmap.PixelWidth * 4, 0);
            //canvas.Children.Remove(shape);

            //BitmapEncoder encoder = new BmpBitmapEncoder();
            //encoder.Frames.Add(BitmapFrame.Create(writeableBitmap));
            //using (FileStream fileStream = new FileStream("C:\\Users\\Женя\\Desktop\\Picture.bmp", FileMode.Create))
            //{
            //    encoder.Save(fileStream);
            //    fileStream.Close();
            //}
            //return true;
        }

        public void RenderShape(Shape shape)
        {
            Size size = new Size(shape.Width, shape.Height);
            shape.Arrange(new Rect(new Size(shape.Width, shape.Height)));
            RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap((int)shape.Width, (int)shape.Height, 96, 96, PixelFormats.Pbgra32);
            renderTargetBitmap.Render(shape);
            byte[] shapePixels = new Byte[(int)shape.Width * (int)shape.Height * 4];
            renderTargetBitmap.CopyPixels(shapePixels, (int)shape.Width * 4, 0);



            BitmapEncoder encoder = new BmpBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
            using (FileStream fileStream = new FileStream("D:\\Picture.jpg", FileMode.Create))
            {
                encoder.Save(fileStream);
                fileStream.Close();
            }




            int x = (int)Canvas.GetLeft(shape), y = (int)Canvas.GetTop(shape);
            for (int i = 0; i < shape.Width; ++i)
                for (int j = 0; j < shape.Height; ++j)
                {
                    int firstPixelsByteNumber = FirstByteNumber(PixelNumber(new Point(i, j), (int)shape.Width));
                    int firstBufferByrteNumber = FirstByteNumber(PixelNumber(new Point(i + x, j + y), writeableBitmap.PixelWidth));
                    byte alpha = shapePixels[firstPixelsByteNumber + 3];
                    if (alpha != 0)
                    {
                        pixelBuffer[firstBufferByrteNumber] = (byte)(shapePixels[firstPixelsByteNumber] * alpha / 255);
                        pixelBuffer[firstBufferByrteNumber + 1] = (byte)(shapePixels[firstPixelsByteNumber + 1] * alpha / 255);
                        pixelBuffer[firstBufferByrteNumber + 2] = (byte)(shapePixels[firstPixelsByteNumber + 2] * alpha / 255);
                        pixelBuffer[firstBufferByrteNumber + 3] = alpha;
                    }
                }
            for (int i = 0; i < shape.Height; ++i)
            {
                int bufferOffset = FirstByteNumber(PixelNumber(new Point(x, y + i), writeableBitmap.PixelWidth));
                writeableBitmap.WritePixels(new Int32Rect(x, y + i, (int)shape.Width, 1), pixelBuffer, (int)shape.Width * 4, bufferOffset);
            }

            

            //int x = (int)Canvas.GetLeft(shape), y = (int)Canvas.GetTop(shape);
            //int offset = FirstByteNumber(PixelNumber(new Point(x, y), writeableBitmap.PixelWidth));
            //for (int i = x; i < x + shape.Width; ++i)
            //{
            //    int bufferOffset = 4 * i * writeableBitmap.PixelWidth;
            //    for (int j = 0; j < renderTargetBitmap.PixelWidth * 4; j += 4)
            //    {
            //        byte alpha = shapePixels[j + 3];
            //        if (alpha != 0)
            //        {
            //            pixelBuffer[j + bufferOffset] = (byte)(shapePixels[j] * alpha / 255);
            //            pixelBuffer[j + bufferOffset + 1] = (byte)(shapePixels[j + 1] * alpha / 255);
            //            pixelBuffer[j + bufferOffset + 2] = (byte)(shapePixels[j + 2] * alpha / 255);
            //            pixelBuffer[j + bufferOffset + 3] = alpha;
            //        }
            //    }
            //}
            //for (int i = 0; i < shape.Height; ++i)
            //    writeableBitmap.WritePixels(new Int32Rect(x, y + i, (int)shape.Width, 1), pixelBuffer, (int)shape.Width * 4, writeableBitmap.PixelWidth * 4 * i + offset);
        }

        public List<Point> FillArea(Point point) // point - точка, с которой начинается заливка
        {
            List<Point> points = new List<Point>();
            Color color = DetermineColor(point);
            
            return points;
        }

        public static WriteableBitmap ResizeWriteableBitmap(WriteableBitmap wBitmap, int reqWidth, int reqHeight)
        {
            int Stride = wBitmap.PixelWidth * ((wBitmap.Format.BitsPerPixel + 7) / 8);
            int NumPixels = Stride * wBitmap.PixelHeight;
            ushort[] ArrayOfPixels = new ushort[NumPixels];


            wBitmap.CopyPixels(ArrayOfPixels, Stride, 0);

            int OriWidth = (int)wBitmap.PixelWidth;
            int OriHeight = (int)wBitmap.PixelHeight;

            double nXFactor = (double)OriWidth / (double)reqWidth;
            double nYFactor = (double)OriHeight / (double)reqHeight;

            double fraction_x, fraction_y, one_minus_x, one_minus_y;
            int ceil_x, ceil_y, floor_x, floor_y;

            ushort pix1, pix2, pix3, pix4;
            int nStride = reqWidth * ((wBitmap.Format.BitsPerPixel + 7) / 8);
            int nNumPixels = reqWidth * reqHeight;
            ushort[] newArrayOfPixels = new ushort[nNumPixels];

            for (int y = 0; y < reqHeight; y++)
            {
                for (int x = 0; x < reqWidth; x++)
                {
                    // Setup
                    floor_x = (int)Math.Floor(x * nXFactor);
                    floor_y = (int)Math.Floor(y * nYFactor);

                    ceil_x = floor_x + 1;
                    if (ceil_x >= OriWidth) ceil_x = floor_x;

                    ceil_y = floor_y + 1;
                    if (ceil_y >= OriHeight) ceil_y = floor_y;

                    fraction_x = x * nXFactor - floor_x;
                    fraction_y = y * nYFactor - floor_y;

                    one_minus_x = 1.0 - fraction_x;
                    one_minus_y = 1.0 - fraction_y;

                    pix1 = ArrayOfPixels[floor_x + floor_y * OriWidth];
                    pix2 = ArrayOfPixels[ceil_x + floor_y * OriWidth];
                    pix3 = ArrayOfPixels[floor_x + ceil_y * OriWidth];
                    pix4 = ArrayOfPixels[ceil_x + ceil_y * OriWidth];

                    ushort g1 = (ushort)(one_minus_x * pix1 + fraction_x * pix2);
                    ushort g2 = (ushort)(one_minus_x * pix3 + fraction_x * pix4);
                    ushort g = (ushort)(one_minus_y * (double)(g1) + fraction_y * (double)(g2));
                    newArrayOfPixels[y * reqWidth + x] = g;
                }
            }
            /*End of Core Part*/
            WriteableBitmap newWBitmap = new WriteableBitmap(reqWidth, reqHeight, 96, 96, PixelFormats.Gray16, null);
            Int32Rect Imagerect = new Int32Rect(0, 0, reqWidth, reqHeight);
            int newStride = reqWidth * ((PixelFormats.Gray16.BitsPerPixel + 7) / 8);
            newWBitmap.WritePixels(Imagerect, newArrayOfPixels, newStride, 0);
            return newWBitmap;
        }
    }
}
